// 该类是一个缓冲区的设计，实现通信过程中用户态的接收缓冲区和发送缓冲区功能，类似于文件的缓冲区
#pragma once
#include "Log.hpp"

#define BUFFER_SIZE 1024
class Buffer
{
private:
    std::vector<char> _buffer; // 使⽤vector进⾏内存空间管理
    uint64_t _read_idx;        // 读偏移
    uint64_t _write_idx;       // 写偏移

public:
    Buffer() : _read_idx(0), _write_idx(0), _buffer(BUFFER_SIZE) {}

    // 获得数组起始位置，需要注意这里并不是对象的位置
    // void* Begin()
    char *Begin()
    {
        return &(*_buffer.begin());
    }

    // 获取当前写⼊起始地址, _buffer的空间起始地址，加上写偏移量
    char *WritePosition()
    {
        return Begin() + _write_idx;
    }

    // 获取当前读取起始地址
    char *ReadPosition()
    {
        return Begin() + _read_idx;
    }

    // 获取缓冲区末尾空闲空间大小--写偏移之后的空闲空间, 总体空间大小减去写偏移
    uint64_t TailSize()
    {
        return _buffer.size() - _write_idx;
        // 这里也可以用_buffer.capacity() - _write_idx
        // 因为vector构造使用的函数即开辟空间也初始化了
        // 也就是说size()和capacity()是一样的大小
        // 后续也用resize()会调整空间
    }

    // 获取缓冲区起始空闲空间大小--读偏移之前的空闲空间
    uint64_t HeadSize()
    {
        return _read_idx;
    }

    // 获取可读数据⼤⼩ = 写偏移 - 读偏移
    uint64_t ReadAbleSize()
    {
        return _write_idx - _read_idx;
    }

    // 确保可写空间⾜够（整体空闲空间够了就移动数据，否则就扩容）
    void EnsureWriteSpace(uint64_t len)
    {
        // 情况1：末尾空闲空间⼤⼩⾜够，直接返回
        if (TailSize() >= len)
            return;
        // 情况2：末尾空闲空间不够，则判断加上起始位置的空闲空间大小是否足够, 够了就将数据移动到起始位置
        if (HeadSize() + TailSize() >= len)
        {
            // 将数据移动到起始位置
            // 把当前数据大小先保存起来
            uint64_t size = ReadAbleSize();
            // 把可读数据拷贝到起始位置
            // 使用C++中copy函数：从一个容器复制元素到另一个容器
            std::copy(ReadPosition(), ReadPosition() + size, Begin());
            // 将读偏移归0
            _read_idx = 0;
            // 将写位置置为可读数据大小，因为当前的可读数据大小就是写偏移量
            _write_idx = size;
        }
        else
        {
            // 情况3：总体空间不够，则需要扩容，不移动数据，直接给写偏移之后扩容足够空间即可
            _buffer.resize(_write_idx + len);
        }
    }

    // 将读偏移向后移动
    void MoveReadOffset(uint64_t len)
    {
        if (len == 0)
            return;
        assert(len <= ReadAbleSize());
        _read_idx += len;
    }

    // 将写偏移向后移动
    void MoveWriteOffset(uint64_t len)
    {
        // 向后移动的大小，必须小于当前后边的空闲空间大小
        assert(len <= TailSize());
        _write_idx += len;
    }

    // 写入数据
    void Write(const void *data, uint64_t len)
    {
        // 1. 保证有足够空间
        if (len == 0)
            return;
        EnsureWriteSpace(len);
        // 2. 拷⻉数据进去
        // 地址强制转换
        const char *d = (const char *)data; 
        // copy是一个模板函数，类型需要匹配
        std::copy(d, d + len, WritePosition());
    }

    // 写入数据并改变写偏移量
    void WriteAndPush(const void *date, uint64_t len)
    {
        Write(date, len);
        MoveWriteOffset(len);
    }

    // 写入字符串
    void WriteString(const std::string &data)
    {
        return Write(data.c_str(), data.size());
    }

    // 写入字符串并且改变写偏移量
    void WriteStringAndPush(const std::string &data)
    {
        WriteString(data);
        MoveWriteOffset(data.size());
    }

    // 将一个Buffer对象中的数据写到本对象的buffer中
    void WriteBuffer(Buffer &data)
    {
        return Write(data.ReadPosition(), data.ReadAbleSize());
    }

    // 将buffer对象写入并且改变写偏移量
    void WriteBufferAndPush(Buffer &data)
    {
        WriteBuffer(data);
        MoveWriteOffset(data.ReadAbleSize());
    }

    // 读取数据
    // buff为输出型参数 将内容从函数中带出
    void Read(void *buff, uint64_t len)
    {
        // 要求要获取的数据大小必须⼩于可读数据大小
        assert(len <= ReadAbleSize());
        std::copy(ReadPosition(), ReadPosition() + len, (char *)buff);
    }

    // 读并且改变读偏移量
    void ReadAndPop(void *buff, uint64_t len)
    {
        Read(buff, len);
        MoveReadOffset(len);
    }

    // 读字符串
    std::string ReadString(uint64_t len)
    {
        // 要求要获取的数据大小必须小于可读数据大小
        assert(len <= ReadAbleSize());
        std::string str;
        str.resize(len);
        // str.c_str()返回的是const char*所以不使用
        Read(&str[0], len);
        return str;
    }

    // 读字符串并且改变读偏移量
    std::string ReadStringAndPop(uint64_t len)
    {
        assert(len <= ReadAbleSize());
        std::string str = ReadString(len);
        MoveReadOffset(len);
        return str;
    }

    char *FindCRLE()
    {
        char *p = (char *)memchr(ReadPosition(), '\n', ReadAbleSize());
        return p;
    }

    // 通常获取⼀⾏数据，这种情况针对是
    std::string GetLine()
    {
        char *p = FindCRLE();
        if (p == nullptr)
            return "";
        return ReadString(p - ReadPosition() + 1); //+是为了把\n也读出来
    }

    std::string GetLineAndPop()
    {
        std::string str = GetLine();
        MoveReadOffset(str.size());
        return str;
    }

    void Clear()
    {
        _read_idx = 0;
        _write_idx = 0;
    }
};